package com.daydreamdev.secondskill.middle.rabbit2;

import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.retry.ImmediateRequeueMessageRecoverer;
import org.springframework.amqp.rabbit.retry.MessageRecoverer;
import org.springframework.amqp.rabbit.retry.RepublishMessageRecoverer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

//https://blog.51cto.com/u_15219956/2958436
@Configuration
public class RabbitConfig {

    @Value("${mq.queueBinding.queue}")
    private String queueName;
    @Value("${mq.queueBinding.exchange.name}")
    private String exchangeName;
    @Value("${mq.queueBinding.key}")
    private String key;

    /**
     * 业务队列
     *
     * @return
     */
    @Bean
    public Queue payQueue() {
        Map<String, Object> params = new HashMap<>();

        //---死信配置，不需要则注释掉
        // 启动服务之前，需要将之前创建的队列进行删除，因为本次队列的配置有所改动。启动成功，可以看到同时创建了业务队列以及死信队列、业务交换机、死信交换机。
        //在业务队列上出现了 DLX 以及 DLK 的标识，代表已经绑定了死信交换机以及死信路由键，此时调用生产者发送消息，
        // 消费者在重试5次后，由于MessageCover默认的实现类是RejectAndDontRequeueRecoverer，
        // 又因为业务队列绑定了死信队列，因此消息会从业务队列中删除，同时发送到死信队列中。
        //声明当前队列绑定的死信交换机
        params.put("x-dead-letter-exchange", dlTopicExchange);
        //声明当前队列的死信路由键
        params.put("x-dead-letter-routing-key", dlRoutingKey);
        //---死信配置，不需要则注释掉

        return QueueBuilder.durable(queueName).withArguments(params).build();
    }

    //--- 默认
    @Bean
    public TopicExchange payTopicExchange() {
        return new TopicExchange(exchangeName, true, false);
    }

    //队列与交换机进行绑定
    @Bean
    public Binding BindingPayQueueAndPayTopicExchange(Queue payQueue, TopicExchange payTopicExchange) {
        return BindingBuilder.bind(payQueue).to(payTopicExchange).with(key);
    }

    //--- 创建死信交换机、死信队列以及两者的绑定 https://blog.51cto.com/u_15219956/2958436
    public static final String dlTopicExchange = "dl-topic-exchange";
    public static final String dlQueue = "dl-queue";
    public static final String dlRoutingKey = "dl-routing-key";

    //创建交换机
    @Bean
    public TopicExchange dlTopicExchange() {
        return new TopicExchange(dlTopicExchange, true, false);
    }

    //创建队列
    @Bean
    public Queue dlQueue() {
        return new Queue(dlQueue, true);
    }

    //队列与交换机进行绑定
    @Bean
    public Binding BindingDlQueueAndExchange(Queue dlQueue, TopicExchange dlTopicExchange) {
        //                     bindingKey                       routingKey
        return BindingBuilder.bind(dlQueue).to(dlTopicExchange).with(dlRoutingKey);
    }
    //topic
    // topic类型的Exchange路由会把消息路由到binding key与routing key相匹配的Queue中。
    // 匹配规则   当binding key与routing key相匹配时，消息将会被路由到对应的Queue中。
    // ①routing key为一个英文句点号‘·’分隔的字符串
    // ②binding key与routing key一样也是‘·’分隔的字符串
    // ③bingding key中可以存在两种特殊字符‘’与‘#’，用于做模糊匹配，其中‘’匹配一个单词，‘#’匹配多个单词（可以是零个）
    // 链接：https://www.jianshu.com/p/64609a208e93
    // 可参考 https://www.yuque.com/yinghuo-8mbbt/kb/cu3uha#YsmHk topic模式binding key与routing key图解


    // //--- 创建异常交换机 RepublishMessageRecoverer https://blog.51cto.com/u_15219956/2958436
    // @Autowired
    // private RabbitTemplate rabbitTemplate;
    //
    //
    // private static String errorTopicExchange = "error-topic-exchange";
    // private static String errorQueue = "error-queue";
    // private static String errorRoutingKey = "error-routing-key";
    //
    // //创建异常交换机
    // @Bean
    // public TopicExchange errorTopicExchange(){
    //     return new TopicExchange(errorTopicExchange,true,false);
    // }
    //
    // //创建异常队列
    // @Bean
    // public Queue errorQueue(){
    //     return new Queue(errorQueue,true);
    // }
    // //队列与交换机进行绑定
    // @Bean
    // public Binding BindingErrorQueueAndExchange(Queue errorQueue,TopicExchange errorTopicExchange){
    //     return BindingBuilder.bind(errorQueue).to(errorTopicExchange).with(errorRoutingKey);
    // }
    //
    //
    // //设置MessageRecoverer
    // @Bean
    // public MessageRecoverer messageRecoverer(){
    //     //AmqpTemplate和RabbitTemplate都可以
    //     return new RepublishMessageRecoverer(rabbitTemplate,errorTopicExchange,errorRoutingKey);
    // }
    //
    // // //--- 创建异常交换机 ImmediateRequeueMessageRecoverer https://blog.51cto.com/u_15219956/2958436
    // // @Bean
    // // public MessageRecoverer messageRecoverer(){
    // //     return new ImmediateRequeueMessageRecoverer();
    // // }


    @Bean("testFactory1")
    public SimpleRabbitListenerContainerFactory testFactory(ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setDeBatchingEnabled(true);//设置批量
        factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);//设置消息为手动确认
        return factory;
    }

    // /**
    //  * https://blog.csdn.net/doupengzp/article/details/106929216
    //  * 每个rabbitTemplate方法只可以有一个回调，不然会报错 only one ConfirmCallback is supported by each RabbitTemplate，解决办法是配成多利的
    //  */
    // @Bean
    // public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
    //     RabbitTemplate template = new RabbitTemplate(connectionFactory);
    //     template.setConfirmCallback(new Callback());//成功回调
    //     template.setMandatory(true);// 开启mandatory模式（开启失败回调）
    //     template.setReturnCallback(new Callback());//失败回调
    //     return template;
    // }


}